package xyz.syyrjx.provider.handler;

import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.SneakyThrows;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import xyz.syyrjx.provider.SyyrjxRpcProviderClass;
import xyz.syyrjx.provider.exception.SyyrjxRpcProviderException;
import xyz.syyrjx.request.SyyrjxRpcCallRequest;
import xyz.syyrjx.response.SyyrjxRpcResponse;
import xyz.syyrjx.exception.SyyrjxRpcRemoteException;
import xyz.syyrjx.code.SyyrjxRpcCoder;
import xyz.syyrjx.entity.SyyrjxRpcServicesTable;
import xyz.syyrjx.provider.filter.SyyrjxRpcProviderServerFilterLinkedList;

import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import java.util.concurrent.*;


/**
 * @author 大地崩坏苍蝇兽
 * @date 2023/1/6 - 18:47
 */
@NoArgsConstructor
@Data
@ChannelHandler.Sharable
public class SyyrjxRpcProviderServerHandler extends SimpleChannelInboundHandler<String> {

    private final static Logger LOGGER = LoggerFactory.getLogger(SyyrjxRpcProviderServerHandler.class);

    private SyyrjxRpcProviderServerFilterLinkedList filterLinkedList = new SyyrjxRpcProviderServerFilterLinkedList();

    private SyyrjxRpcProviderClass provider;

    private Executor threadPool = new ThreadPoolExecutor(10, 15, 30, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(10), new RejectedExecutionHandler() {
        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
            throw new SyyrjxRpcProviderException(SyyrjxRpcProviderException.PROVIDER_BUSY);
        }
    });

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String str) throws Exception {
        ProviderProviderRunnable runnable = new ProviderProviderRunnable();
        runnable.setStr(str);
        runnable.setCtx(ctx);
        this.threadPool.execute(runnable);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
    }

    @Data
    class ProviderProviderRunnable implements Runnable{

        private String str;

        private ChannelHandlerContext ctx;

        @SneakyThrows
        @Override
        public void run() {
            SyyrjxRpcCallRequest request = null;
            SyyrjxRpcResponse response = null;
            try {
                request = SyyrjxRpcCoder.StringToObject(str);
                LOGGER.debug("服务端接收到的请求" + request);
                if (!filterLinkedList.preVerify(request)) {
                    response = SyyrjxRpcResponse.providerFail(SyyrjxRpcRemoteException.ACCESS_MISTAKE);
                    request.setFutureId(request.getFutureId());
                    return;
                }
                SyyrjxRpcServicesTable table = provider.getProviderMap().get(request.getProviderName()).get(request.getMethodName());

                Object[] params = request.getParam();

                Object resObj = table.getMethod().invoke(table.getBean(), params);
                response = SyyrjxRpcResponse.success(resObj);
                response.setFutureId(request.getFutureId());

            }catch (InvocationTargetException e) {
                LOGGER.debug("服务提供者调用出错，发送错误信息" + e + "到" + ctx);
                response = SyyrjxRpcResponse.providerFail(e.getCause());
                response.setFutureId(request.getFutureId());
                throw e;
            } finally {
                LOGGER.debug("准备返回给消费者的数据为" + response);
                ctx.writeAndFlush(response.toSerializableString());
            }
        }
    }
}
